﻿using System;
using System.Diagnostics;
using System.ServiceModel;

namespace ServerUtils
{
    public class ServiceHostEx<TService> : LogGenerator, IDisposable where TService : WCFService, new()
    {
        private ServiceHost serviceHost = null;
        private readonly TService singletonInstance;
        private readonly Uri[] baseAddresses;
        private bool disposed = false;

        /// <summary>
        /// Initializes a new instance of the ServiceHostEx class with the type of service and its base addresses specified.
        /// </summary>
        /// <param name="baseAddresses">An System.Array of type System.Uri that contains the base addresses for the hosted service.</param>
        public ServiceHostEx(params Uri[] baseAddresses)
        {
            this.baseAddresses = baseAddresses;
        }

        /// <summary>
        /// Initializes a new instance of the ServiceHostEx class with the type of service and its base addresses specified.
        /// </summary>
        /// <param name="singletonInstance">The instance of the hosted service.</param>
        /// <param name="baseAddresses">An System.Array of type System.Uri that contains the base addresses for the hosted service.</param>
        public ServiceHostEx(TService singletonInstance, params Uri[] baseAddresses)
        {
            this.singletonInstance = singletonInstance;
            this.singletonInstance.Error += this.ServiceError;
            this.singletonInstance.Fault += this.ServiceFault;
            this.baseAddresses = baseAddresses;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public ServiceHost ServiceHost
        {
            get { return this.serviceHost; }
        }

        public TService SingletonInstance
        {
            get { return this.singletonInstance; }
        }

        public void Open()
        {
            if (this.serviceHost != null)
            {
                return;
            }

            if (this.singletonInstance == null)
            {
                this.serviceHost = new ServiceHost(typeof(TService), this.baseAddresses);
            }
            else
            {
                this.serviceHost = new ServiceHost(this.singletonInstance, baseAddresses);
            }
            
            this.SubscribeToServiceHostEvents();
            this.serviceHost.Open();
        }

        public void Close()
        {
            if (this.serviceHost == null)
            {
                return;
            }

            this.serviceHost.Close();
            this.serviceHost = null;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
            {
                return;
            }

            if (disposing)
            {
                this.Close();
            }

            disposed = true;
        }

        private void ServiceFault(object sender, FaultEventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.Error, e.Error.ToString()));
        }

        private void ServiceError(object sender, ErrorEventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.Error, e.Error.ToString()));
        }

        private void ServiceHostUnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.Warning, "Unknown message is received. {0}", e.Message));
        }

        private void ServiceHostOpened(object sender, EventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.SuccessAudit, "Service host opened"));
        }

        private void ServiceHostFaulted(object sender, EventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.Error, "Service host faulted"));
        }

        private void ServiceHostClosing(object sender, EventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.SuccessAudit, "Service host closing"));
        }

        private void ServiceHostClosed(object sender, EventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.SuccessAudit, "Service host closed"));
        }

        private void ServiceHostOpening(object sender, EventArgs e)
        {
            this.OnLog(new LogEventArgs(EventLogEntryType.SuccessAudit, "Service host opening"));
        }

        private void SubscribeToServiceHostEvents()
        {
            this.serviceHost.Closed += this.ServiceHostClosed;
            this.serviceHost.Closing += this.ServiceHostClosing;
            this.serviceHost.Faulted += this.ServiceHostFaulted;
            this.serviceHost.Opened += this.ServiceHostOpened;
            this.serviceHost.Opening += this.ServiceHostOpening;
            this.serviceHost.UnknownMessageReceived += this.ServiceHostUnknownMessageReceived;
        }
    }
}
